// keyfeatures_example1.cpp                                           -*-C++-*-

#include <ball_loggermanager.h>
#include <ball_recordstringformatter.h>
#include <ball_streamobserver.h>
#include <ball_scopedattribute.h>
#include <bsl_iostream.h>

using namespace BloombergLP;

///Key Example 1: Writing to a Log
///- - - - - - - - - - - - - - - -
// The following trivial example shows how to use the logging macros to log
// messages at various levels of severity.
//
// First, we include 'ball_log.h', then create an example function where we
// initialize the log category within the context of this function.
// The logging macros such as 'BALL_LOG_ERROR' will not compile unless a
// category has been specified in the current lexical scope:
//..
    #include <ball_log.h>

    int processData() {
        BALL_LOG_SET_CATEGORY("MYLIBRARY.MYSUBSYSTEM");
//..
// Then, we record messages at various levels of severity.  These messages will
// be conditionally written to the log depending on the current logging
// threshold of the category (configured using the 'ball::LoggerManager'
// singleton):
//..
        BALL_LOG_FATAL << "Write this message to the log if the log threshold "
                       << "is above 'ball::Severity::e_FATAL' (i.e., 32).";
//
        BALL_LOG_TRACE << "Write this message to the log if the log threshold "
                       << "is above 'ball::Severity::e_TRACE' (i.e., 192).";
//..
// Next, we demonstrate how to use proprietary code within logging macros.
// Suppose you want to add the content of a vector to the log trace:
//..
        bsl::vector<int> myVector(4, 328);
        BALL_LOG_TRACE_BLOCK {
            BALL_LOG_OUTPUT_STREAM << "myVector = [ ";
            unsigned int position = 0;
            for (bsl::vector<int>::const_iterator it  = myVector.begin(),
                                                  end = myVector.end();
                 it != end;
                 ++it, ++position) {
                BALL_LOG_OUTPUT_STREAM << position << ':' << *it << ' ';
            }
            BALL_LOG_OUTPUT_STREAM << ']';
        }
    }
//..
// Notice that the code block will be conditionally executed depending on the
// current logging threshold of the category.  The code within the block must
// not produce any side effects, because its execution depends on the current
// logging configuration.  The special macro 'BALL_LOG_OUTPUT_STREAM' provides
// access to the log stream within the block.
//
// Then we show a simple class that declares a log category for the class (log
// categories can also be configured at namespace scope in a '.cpp' file):
//..
    class AccountInformation {
        BALL_LOG_SET_CLASS_CATEGORY("MYLIBRARY.AccountInformation");

        void privateRetrieveData();
      public:
        void addSecurity(const bsl::string_view& security);
        void removeSecurity(const bsl::string_view& security);
    };

    void AccountInformation::addSecurity(const bsl::string_view& security)
    {
        BALL_LOG_INFO << "addSecurity";
    }
//..
// Finally we can use a 'ball::ScopedAttribute' to associate an attribute with
// the current thread's logging context.
//..
    void AccountInformation::privateRetrieveData()
    {
        BALL_LOG_INFO << "retrieveData";
    }

    void AccountInformation::removeSecurity(const bsl::string_view& security)
    {
        ball::ScopedAttribute securityAttribute("mylibrary.security", security);
        BALL_LOG_INFO << "removeSecurity";

        privateRetrieveData();
    }
//..
// Notice that the attribute "mylibrary.security" will be associated with each
// log message generated by the current thread until the destruction of the
// 'securityAttribute' object (including the log message created by
// 'privateRetrieveData').  To publish the attribute to the log the
// 'ball::Observer' must be configured correctly (e.g., using the "%a" format
// specification with 'ball::FileObserver' or 'ball::RecordStringFormatter'), as
// we do in the subsequent example.

    using namespace BloombergLP;
    int main()
    {
        ball::LoggerManagerConfiguration configuration;
        configuration.setDefaultThresholdLevelsIfValid(ball::Severity::e_TRACE);
        ball::LoggerManagerScopedGuard guard(configuration);
        bslma::Allocator *alloc = bslma::Default::globalAllocator(0);

        bsl::shared_ptr<ball::StreamObserver> observer =
            bsl::allocate_shared<ball::StreamObserver>(alloc, &bsl::cout);
        ball::LoggerManager::singleton().registerObserver(observer, "default");
        observer->setRecordFormatFunctor(ball::RecordStringFormatter(
                                          "\n%d %p:%t %s %a %m\n"));
        processData();
        AccountInformation info;
        info.addSecurity("IBM");
        info.removeSecurity("IBM");

        return 0;
    }

